package ca.eloas.judging.rest.module;

import ca.eloas.judging.model.ModelObjectFactory;
import ca.eloas.judging.objectify.ObjecticyServiceImpl;
import ca.eloas.judging.objectify.PersistenceService;
import ca.eloas.judging.rest.operations.ChildLink;
import ca.eloas.judging.rest.operations.CopyToMessage;
import ca.eloas.judging.rest.operations.JudgingOprationFactory;
import ca.eloas.judging.rest.operations.SelfLink;
import ca.eloas.judging.rest.repositories.CompetitionRepository;
import ca.eloas.judging.rest.repositories.JudgeRepository;
import ca.eloas.judging.rest.repositories.OfyCompetitionRepository;
import ca.eloas.judging.rest.repositories.OfyJudgeRepository;
import ca.eloas.judging.rest.repositories.OfyScoreRepository;
import ca.eloas.judging.rest.repositories.OfyTeamRepository;
import ca.eloas.judging.rest.repositories.ScoreRepository;
import ca.eloas.judging.rest.repositories.TeamRepository;
import ca.eloas.judging.rest.resources.BootResource;
import ca.eloas.judging.rest.resources.CheckResource;
import ca.eloas.judging.rest.resources.CompetitionResource;
import ca.eloas.judging.rest.resources.JudgeResource;
import ca.eloas.judging.rest.resources.PopulateResource;
import ca.eloas.judging.rest.resources.ResourceFactory;
import ca.eloas.judging.rest.resources.SessionsResource;
import ca.eloas.judging.rest.resources.TeamResource;
import ca.eloas.judging.rest.security.AuthenticationAuthorizationService;
import ca.eloas.judging.rest.security.AuthenticationAuthorizationServiceImpl;
import ca.eloas.judging.rest.security.PageSecurityFilter;
import ca.eloas.judging.rest.transactions.Transactional;
import ca.eloas.judging.rest.transactions.TransactionalInterceptor;
import ca.eloas.restsupport.ObjectFactory;
import ca.eloas.restsupport.utils.CurrentMethodInterceptor;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.appengine.labs.repackaged.com.google.common.collect.ImmutableMap;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.matcher.Matchers;
import com.google.web.bindery.autobean.shared.AutoBean;
import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
import com.googlecode.objectify.Objectify;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import freemarker.ext.servlet.FreemarkerServlet;

import javax.inject.Singleton;
import javax.ws.rs.Path;
import java.util.ArrayList;
import java.util.Collection;

public class WebModule extends JerseyServletModule {

    @Override
    protected void configureServlets() {


        bind(CheckResource.class);
        bind(CompetitionResource.class);
        bind(TeamResource.class);
        bind(BootResource.class);
        bind(JudgeResource.class);
        bind(SessionsResource.class);


        bind(PopulateResource.class);

        bind(CompetitionRepository.class).to(OfyCompetitionRepository.class);
        bind(TeamRepository.class).to(OfyTeamRepository.class);
        bind(JudgeRepository.class).to(OfyJudgeRepository.class);
        bind(ScoreRepository.class).to(OfyScoreRepository.class);

        bind(AuthenticationAuthorizationService.class).to(AuthenticationAuthorizationServiceImpl.class);

        serve("/rest/*").with(GuiceContainer.class);
        serve("*.html").with(FreemarkerServlet.class, ImmutableMap.of("TemplatePath", "/"));
        filter("*.html").through(PageSecurityFilter.class);

        bind(new TypeLiteral<PersistenceService<Objectify>>() {
        }).to(ObjecticyServiceImpl.class);

        install(new FactoryModuleBuilder()
                .build(ResourceFactory.class));

        install(new FactoryModuleBuilder()
                .build(JudgingOprationFactory.class));
        requestStaticInjection(SelfLink.class);
        requestStaticInjection(CopyToMessage.class);
        requestStaticInjection(ChildLink.class);



        CurrentMethodInterceptor methodInterceptor = new CurrentMethodInterceptor();
        requestInjection(methodInterceptor);

        //TODO add mongoInterceptor when load latest questionnaire in participation will be loaded by mongo again
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Path.class), methodInterceptor);
        TransactionalInterceptor transactionalInterceptor = new TransactionalInterceptor();
        requestInjection(transactionalInterceptor);
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionalInterceptor);
    }


    @Provides
    @Singleton
    FreemarkerServlet createFreeMarker() {

        return new FreemarkerServlet();
    }

    @Provides
    @Singleton
    ObjectFactory objectFactory() {

        return new MyObjectFactory();
    }

    @Provides
    @Singleton
    UserService getUserService() {

        return UserServiceFactory.getUserService();
    }

    private static class MyObjectFactory implements ObjectFactory {
        public <T> T create(Class<T> cls) {

            AutoBean<T> bean = AutoBeanFactorySource.create(ModelObjectFactory.class).create(cls);
            bean.accept(new AutoBeanVisitor() {
                @Override
                public boolean visitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, CollectionPropertyContext ctx) {
                    if ( value == null ) {
                        ctx.set(new ArrayList());
                    }
                    return true;
                }
            });

            return bean.as();
        }
    }
}

